﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DcsGauges.Gui.Gauges.Interfaces;
using DcsGauges.Gui.Rendering;
using Microsoft.Xna.Framework.Graphics;
using DcsGauges.DataStructures;
using Microsoft.Xna.Framework;
using DcsGauges.Shared.DataStructures;
using DcsGauges.Gui.Gauges.ValueConverters;

namespace DcsGauges.Gui.Gauges.Concrete
{
    class HsiGauge : GuiRenderableBase, IMultiValueGauge
    {
        private Texture2D                           backgroundTexture                           = null;
        private Texture2D                           compassRotatorTexture                       = null;
        private Texture2D                           bearing1Texture                             = null;
        private Texture2D                           bearing2Texture                             = null;
        private Texture2D                           courseArrowTexture                          = null;
        private Texture2D                           warningOffTexture                           = null;
        private ThreeAnalogNumberGauge              rangeIndicator                              = null;
        private ThreeAnalogNumberGauge              courseIndicator                             = null;

        private Dictionary<DcsProperty, float>    values                                      = new Dictionary<DcsProperty,float>();

        private static readonly Dictionary<DcsProperty, IGaugeValueConverter<float>> valueConverters = 
            new Dictionary<DcsProperty,IGaugeValueConverter<float>>()
            {
                { DcsProperties.HoriziontalSituationIndicator.Bearing1,        new LinearConverter(new InOutMapping(0, MathEx.Deg2Rad(0.0f)), new InOutMapping(1.0f, MathEx.Deg2Rad(360.0f))) },
                { DcsProperties.HoriziontalSituationIndicator.Bearing2,        new LinearConverter(new InOutMapping(0, MathEx.Deg2Rad(0.0f)), new InOutMapping(1.0f, MathEx.Deg2Rad(360.0f))) },
                { DcsProperties.HoriziontalSituationIndicator.Compass,         new LinearConverter(new InOutMapping(0, MathEx.Deg2Rad(0.0f)), new InOutMapping(1.0f, MathEx.Deg2Rad(360.0f))) },
                { DcsProperties.HoriziontalSituationIndicator.CourseArrow,     new LinearConverter(new InOutMapping(0, MathEx.Deg2Rad(0.0f)), new InOutMapping(1.0f, MathEx.Deg2Rad(360.0f))) },
                { DcsProperties.HoriziontalSituationIndicator.PowerOffWarning, new VoidValueConverter() },
            };

        public HsiGauge(GuiRenderableBase parent, URect area, GraphicsDevice device) : base(parent, area)
        {
            this.backgroundTexture                  = TextureManager.GetTexture("Textures/Gauges/Backgrounds/hsiback");
            this.compassRotatorTexture              = TextureManager.GetTexture("Textures/Gauges/Backgrounds/hsicompass");
            this.bearing1Texture                    = TextureManager.GetTexture("Textures/Gauges/Needles/HsiBearing1");
            this.bearing2Texture                    = TextureManager.GetTexture("Textures/Gauges/Needles/HsiBearing2");
            this.courseArrowTexture                 = TextureManager.GetTexture("Textures/Gauges/Needles/HsiCourseArrow");
            this.warningOffTexture                  = TextureManager.GetTexture("Textures/Gauges/Foregrounds/HsiOff");

#warning not mapped correctly atm. Research with DCS is needed. Maybe ValueConverter chaining is required here 3 -> 1 -> 3 digit values???
            this.rangeIndicator                     = new ThreeAnalogNumberGauge(this, new URect(new ULoc(0.05f, 0.17f), new USize(0.15f, 0.13f)), device,
                new Tuple<DcsProperty, IGaugeValueConverter<float>>(DcsProperties.HoriziontalSituationIndicator.Range.Digital1000, new VoidValueConverter()),
                new Tuple<DcsProperty, IGaugeValueConverter<float>>(DcsProperties.HoriziontalSituationIndicator.Range.Digital100,  new VoidValueConverter()),
                new Tuple<DcsProperty, IGaugeValueConverter<float>>(DcsProperties.HoriziontalSituationIndicator.Range.Digital10,  new VoidValueConverter()));
            this.courseIndicator                    = new ThreeAnalogNumberGauge(this, new URect(new ULoc(0.81f, 0.17f), new USize(0.14f, 0.13f)), device, 
                new Tuple<DcsProperty, IGaugeValueConverter<float>>(DcsProperties.HoriziontalSituationIndicator.CourseSelector.Digital100, new VoidValueConverter()),
                new Tuple<DcsProperty, IGaugeValueConverter<float>>(DcsProperties.Unknown, new VoidValueConverter()),
                new Tuple<DcsProperty, IGaugeValueConverter<float>>(DcsProperties.HoriziontalSituationIndicator.CourseSelector.Digital1, new VoidValueConverter()));

            foreach (DcsProperty dp in this.Properties)
            {
                this.values.Add(dp, 0.0f);
            }
        }

        public Shared.DataStructures.DcsProperty[] Properties
        {
            get 
            {
                return valueConverters.Keys.ToArray();   
            }
        }

        public void SetValue(DcsProperty property, float value)
        {
            this.values[property] = value;
        }

        public float GetValue(DcsProperty property)
        {
            float outValue;
            this.values.TryGetValue(property, out outValue);
            return outValue;
        }

        public override void Render(SpriteBatch spriteBatch, GraphicsDevice device, Rectangle parentRect, float globalZIndex)
        {
            Rectangle viewportRect          = this.CalcViewportRect(parentRect, new Rectangle(0, 0, this.backgroundTexture.Width, this.backgroundTexture.Height));
            Rectangle compassRect           = this.CalcViewportRect(new URect(new ULoc(0.2155f, 0.164f), new USize(0.7f, 0.7f)), viewportRect, new Rectangle(0, 0, this.compassRotatorTexture.Width, this.compassRotatorTexture.Height));

            this.rangeIndicator.Render(spriteBatch, device, viewportRect, this.NextBackIndex(globalZIndex, 1));
            this.courseIndicator.Render(spriteBatch, device, viewportRect, this.NextBackIndex(globalZIndex, 1));

            spriteBatch.Draw(this.backgroundTexture, viewportRect, null, Color.White, 0.0f, Vector2.Zero, SpriteEffects.None, globalZIndex);

            Point bearingOrigin             = this.bearing1Texture.Bounds.Center;
            Vector2 position                = new Vector2(compassRect.Center.X, compassRect.Center.Y);
            float bearingScaleX             = (float)compassRect.Width / (float)this.compassRotatorTexture.Width; 
            float bearingScaleY             = (float)compassRect.Height / (float)this.compassRotatorTexture.Height + 0.04f; //arrows have to be bigger than compass rotator itself! (take a fixed constant for now)
            float bearing1Rotation          = valueConverters[DcsProperties.HoriziontalSituationIndicator.Bearing1].Convert(this.values[DcsProperties.HoriziontalSituationIndicator.Bearing1]);
            float bearing2Rotation          = valueConverters[DcsProperties.HoriziontalSituationIndicator.Bearing2].Convert(this.values[DcsProperties.HoriziontalSituationIndicator.Bearing2]);

            spriteBatch.Draw(this.bearing2Texture, position, null, Color.White, bearing2Rotation, new Vector2(bearingOrigin.X, bearingOrigin.Y), new Vector2(bearingScaleX, bearingScaleY), SpriteEffects.None, NextFrontIndex(globalZIndex, 1));
            spriteBatch.Draw(this.bearing1Texture, position, null, Color.White, bearing1Rotation, new Vector2(bearingOrigin.X, bearingOrigin.Y), new Vector2(bearingScaleX, bearingScaleY), SpriteEffects.None, NextFrontIndex(globalZIndex, 2));

            float rotation                  = valueConverters[DcsProperties.HoriziontalSituationIndicator.Compass].Convert(this.GetValue(DcsProperties.HoriziontalSituationIndicator.Compass));
            Point origin                    = this.compassRotatorTexture.Bounds.Center;
            float scaleX                    = (float)compassRect.Width / (float)this.compassRotatorTexture.Width;
            float scaleY                    = (float)compassRect.Height / (float)this.compassRotatorTexture.Height;
            
            spriteBatch.Draw(this.compassRotatorTexture, position, null, Color.White, rotation, new Vector2(origin.X, origin.Y), new Vector2(scaleX, scaleY), SpriteEffects.None, NextFrontIndex(globalZIndex, 3));

            rotation                        = valueConverters[DcsProperties.HoriziontalSituationIndicator.CourseArrow].Convert(this.values[DcsProperties.HoriziontalSituationIndicator.CourseArrow]);
            spriteBatch.Draw(this.courseArrowTexture, position, null, Color.White, rotation, new Vector2(origin.X, origin.Y), new Vector2(scaleX, scaleY), SpriteEffects.None, NextFrontIndex(globalZIndex, 4));

            var warningOffFlagConverter     = new BoolConverter(0.5f);
            bool warningOffFlag             = warningOffFlagConverter.Convert(this.GetValue(DcsProperties.HoriziontalSituationIndicator.PowerOffWarning));
            if (!warningOffFlag)
            {
                Rectangle offFlagRect       = this.CalcViewportRect(new URect(new ULoc(0.91f, 0.34f), new USize(0.06f, 0.15f)), viewportRect, this.warningOffTexture.Bounds);
                spriteBatch.Draw(this.warningOffTexture, offFlagRect, null, Color.White, 0.0f, Vector2.Zero, SpriteEffects.None, this.NextFrontIndex(globalZIndex, 3));
            }
        }
    }
}
